跳到主要内容

Python中的数据结构

数据结构

组合数据类型

有str, tuple, list, dict, set这几种类型 可以用isinstance(x, type)来判断是否是某种数据类型

a = '1234'
print(isinstance(a, str))

1. 列表

# 增
a = [1, 2]
# 直接用加号,这里是生成新的列表,然后给a重新赋值
a = a + [3, 4]
# 用 += ,与加号不一样,这里是在原地修改的
a += [5]
# 用append,直接在原地修改
a.append(6)
#----------------------------------------#
# 删
del a[0]
del a[0: 2]
# 这是在原地删除
#-----------------------------------------#
# 乘法
a = [1, 2]
print([a * 3]) # [[1, 2, 1, 2, 1, 2]]
print(a * 3) # [1, 2, 1, 2, 1, 2]
print([a] * 3) # [[1, 2], [1, 2], [1, 2]]
# 上面这种方法要注意,[a] * 3 形成的每个列表元素都指向了a,其他的没有
  • 列表切片和字符串一样
  • 列表排序
# 用列表中的sort()函数,在原地进行排序
a = [5, 2, 3, 4, 1, 6, 7]
a.sort()
print(a) # [1, 2, 3, 4, 5, 6, 7]
a.sort(reverse=True)
print(a) # [7, 6, 5, 4, 3, 2, 1]
# 用自带的sorted()函数,不会修改原列表
b = sorted(a)
print(a)

#----------------------------#

# 组合数据类型进行排序
student = [
('Mike', 'A', 12),
('Peter', 'D', 13),
('Alice', 'B', 14),
('Json', 'B', 13),
('Mike', 'A', 15)
]
student.sort()
print(student)
# [('Alice', 'B', 14), ('Json', 'B', 13), ('Mike', 'A', 12), ('Mike', 'A', 15), ('Peter', 'D', 13)]

#----------------------------#

# 自定义排序规则
def mykey(x):
# 先按年龄从大到小,再按成绩从高到低,最后按名称字典序
return (-x[2], x[1], x[0])
student.sort(key=mykey)
# [('Mike', 'A', 15), ('Alice', 'B', 14), ('Json', 'B', 13), ('Peter', 'D', 13), ('Mike', 'A', 12)]

# 自定义规则的原理是:对于列表中的每一个元素,都会被当成参数来调用key函数,根据key返回的值来进行排序,而不是根据本来的值进行排序
  • 用列表实现堆栈(append, pop)
  • 用列表实现队列

用列表实现队列效率不高,类似C++中的数组,删除头和在头插入

  • 列表推导式生成列表

[表达式 for语句 [for语句] [if语句]]的形式

  • 嵌套的列表推导式(一个列表推导式生成一个列表,推导式的嵌套生成列表的嵌套)

有时候可以使用zip()来实现一些复杂的流程语句 zip()可以与解包*搭配使用,就是第四部分所提到的解包参数列表中的

martix = [
[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]
]
print(martix)
#[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
trans_martix = list(zip(*martix))
print(trans_martix)
#[(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
  • 列表映射
# 有时候会想将一个列表映射为另一个列表,需要用到map(function, list),这会返回一个延时求值对象
# 所谓延时求值对象,分为三个方面
# 第一,是一个对象,包含了求值操作
# 第二,求值的意思是,将每一个list的元素作为参数调用function,将所求出来的值收集起来;
# 第三,延时的意思是,不会立刻进行求值收集,而是要等到调用list, tuple, set等方法的时候才会求值,但也有例外。

a = [1, 2, 3, 4, 5]
b = map(lambda x: x**2, a)
c = list(b)
d = tuple(b)
print(c) # [1, 4, 9, 16, 25]
print(d) # (),已经求值过后就不能求值了

#-------------------------#

# 可以用于处理输入的数据,这里没有使用list等函数也直接进行了求值操作
x, y, z = map(lambda x: int(x), input().split())
print(x, y, z)

#-------------------------#

# 映射多个序列
a = [1, 2, 3]
b = [2, 3, 4]
c = [100, 20, 100]
r = list(map(lambda x, y, z: x+y+z, a, b, c))
print(r)
# [103, 25, 107]

  • 列表过滤
# 和映射类似,只是收集的值是满足function的要求
s = [1, 2, 4, 5, 6, 7, 9, 10]
b = list(filter(lambda x: x % 2 == 0, s))
print(b)
# [2, 4, 6, 10]
  • 列表拷贝
# a = b 不是拷贝,只是让 b 指向了 a
# 浅拷贝,只拷贝指针
a = [1, [1, 2]]
b = a[:]
print(a) # [1, [1, 2]]
print(b) # [1, [1, 2], 3]
print(b[1] is a[1]) # True

#-----------------------------#

# 深拷贝,只拷贝值,拷贝之后两者没有关系
# 可以利用copy模块中的deepcopy来进行深拷贝
import copy
a = [1, [1, 2]]
b = copy.deepcopy(a)
b[1].append(3)
print(a) # [1, [1, 2]]
print(b) # [1, [1, 2, 3]]
print(a[1] is b[1]) # False
  • 列表,元组,字符串之间的相互转换
# 转为列表只需要用list(), 转为元组只需要用tuple()
hellolist = ['hello', ' ', 'world']
hellostr = 'hello world'
hellotuple1 = tuple(hellostr)
print(hellotuple1) # ('h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd')
hellotuple2 = tuple(hellolist)
print(hellotuple2) # ('hello', ' ', 'world')

# 列表和元组转为字符串需要用"".join(),就是空字符串调用了join方法
l = ['hello', ' ', 'world']
t = ('hello', ' ', 'world')
s1 = "".join(l)
s2 = "".join(t)
print(s1) # hello world
print(s2) # hello world

2. del语句

可以用于移除一个元素,也可用于切片,也可以用于清空列表

3. 元组

  • 用圆括号包裹起来
  • 元组与列表的最大区别在于列表是可变的,而元组是不可变的
  • 可以用多重赋值给一个元组赋值,也可以用序列解包的方式得到元组中的内容
t = 'I', 'you', 'he', 'she', 'it'
print(t)
#('I', 'you', 'he', 'she', 'it')
a, b, c, d, e = t
print(a, b, c, d, e)
#I you he she it
  • 元组的元素是不可修改的,但元组元素中若含有可变类型(如list),则可以修改这个list。 借助指针的思想去思考就是,元组中每个元素都是一个指针,指向了对应的内存,这个指针是不可以修改指向的,指向的内存可以存储任何类型。若存储的类型是列表,列表的每个元素也是一个指针,指向是可以修改的,列表中的指针指向修改,指向别的空间,就会导致列表内容变化。

  • 单元素的元组,一定要加, (如(1,))

  • 用下标访问元组,元组切片,元组的计算(+ *)都与字符串一样

  • 元组赋值时,是创建新元组,因为元组不可以改变

  • 元组生成式和列表类似,但是要求在最前面加上tuple

L = (1, 2, 3, 4)
l = tuple(x*x for x in L)
print(l) # (1, 4, 9, 16)

4. 集合

  • 集合是由不重复元素组成的无序容器。基本用法包括成员检测、消除重复元素。集合对象支持合集、交集、差集、对称差分等数学运算
  • 集合的用途是快速判断某个东西是否在一堆东西里面(用in,时间是固定的,与集合元素个数无关)
  • 集合中的元素是无序的,与构造时的顺序无关,因此集合是不能用下标来访问的
  • 集合中的元素只能是不可变类型,一个元组若是包含可变类型元素,也不能作为集合的元素
  • 集合可以用和set()来创建

注意,创建空集合只能用 set(),不能用 创建的是空字典

  • 集合也可以进行类型推断
  • 集合的运算
# 用字符串构造集合
b = set("abcdefabcdef")
print(b) # {'f', 'c', 'b', 'd', 'a', 'e'}
# 用字典构造集合,只会构造键的集合
d = {'Mike': 19, 'John': 20, 'Peter': 21}
s1 = set(d)
print(s1) # {'John', 'Peter', 'Mike'}
# 用元组,列表构造集合
t = (1, 2, 2, 'Mike', 'hello')
l = [1, 2, 3, 2, 5]
print(set(t)) # {1, 2, 'hello', 'Mike'}
print(set(l)) # {1, 2, 3, 5}

# 真子集
a > b
# 子集
a >= b

#差集
print(b - a) #{'e', 'f'}
#交集
print(a & b) #{'c', 'd', 'b', 'a'}
#并集
print(a | b) #{'c', 'd', 'b', 'e', 'f', 'a'}
#对称差,只在a中,b中单独出现,不能在两个集合中都出现
print(a ^ b) #{'f', 'e'}

注意 a = a - b 与 a -= b

  • 集合的函数
  1. add(x),添加元素x,若x已经存在,则不添加
  2. clear(),清空集合
  3. copy(),返回自身的浅拷贝
  4. remove(x),删除元素x,若x不存在,则引发异常
  5. update(x),将序列x中的元素添加到集合当中

5.字典

  • 键值对的集合,索引是关键字,字典的每个键值对中的值是可以赋值的,意味值是一个指针
  • 关键字必须是不可变类型
  • 字典的主要用途是通过关键字存储、提取值,这个速度是很快的。
  • 用 del 可以删除键值对。
  • 用已存在的关键字存储值,与该关键字关联的旧值会被取代。通过不存在的键提取值,则会报错。若是给一个不存在的键赋值,则会在字典中新增该键值对。
  • 若是在创建字典时,里面有多个重复的键,那么该键最后对应的值便是这个字典中这个键对应的值
  • 字典的创建可以用,也可以用dict()构造函数
  • 字典也可以用推导式
创建字典的方式

#花括号创建字典
dic = {'Mike': 18, 'John': 19, 'Peter':20}
print(dic) #{'Mike': 18, 'John': 19, 'Peter': 20}
print(dic['Mike']) #18
print(dic['John']) #19

#用dict构造函数创建字典
dic1 = dict([('sape', 4139), ('guido', 4127), ('jack', 4098)])
print(dic1)
#{'sape': 4139, 'guido': 4127, 'jack': 4098}
dic2 = dict(sape=4139, guido=4127, jack=4098)
print(dic2)
#{'sape': 4139, 'guido': 4127, 'jack': 4098}

#字典推导式
dic3 = {x: x**2 for x in (2, 4, 6)}
print(dic3)
#{2: 4, 4: 16, 6: 36}

  1. clear(),清空字典
  2. keys(),取字典的键的序列(这个序列并不是list, tuple, set)
  3. items(),取字典的元素的序列
  4. values(),取字典的值的序列
  5. pop(x),删除键为x的元素
  6. get(key[, default]),如果 key 存在于字典中则返回 key 的值,否则返回 default。 如果 default 未给出则默认为 None
  7. copy(),浅拷贝,如果要深拷贝,可以引入一个copy模块,然后调用deepcopy()函数

6. 循环技巧

  • 字典中循环时,用 items() 方法可同时取出键和对应的值
  • 序列中循环时,用 enumerate() 函数可以同时取出位置索引和对应的值
  • 同时循环两个或多个序列时,用 zip() 函数可以将其内的元素一一匹配
  • 逆向循环序列时,先正向定位序列,然后调用 reversed() 函数
  • 使用 set() 去除序列中的重复元素。使用 sorted() 加 set() 则按排序后的顺序,循环遍历序列中的唯一元素

7. 深入条件控制

  • 比较运算符 in 和 not in 用于执行确定一个值是否存在(或不存在)于某个容器中的成员检测
  • 运算符 is 和 is not 用于比较两个对象是否是同一个对象。

所有比较运算符的优先级都一样,且低于任何数值运算符

  • 比较操作支持链式操作。例如,a < b == c 校验 a 是否小于 b,且 b 是否等于 c。
  • 比较操作可以用布尔运算符 and 和 or 组合,并且,比较操作(或其他布尔运算)的结果都可以用 not 取反。

not 的优先级最高, or 的优先级最低

  • 布尔运算符 and 和 or 也称为 短路 运算符

8. 序列和其他类型的比较

  • 序列对象可以与相同序列类型的其他对象比较,这种比较使用字典式顺序
  • 对不同类型的对象来说,只要待比较的对象提供了合适的比较方法,就可以使用 < 和 > 进行比较,否则,解释器不会随便给出一个对比结果,而是触发 TypeError 异常。